package com.zjl.SpringBoot.第03章_Web开发.E_内容协商;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.*;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.WebUtils;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 内容协商原理-HttpMessageConverter
 *      ●HttpMessageConverter 怎么工作?合适工作?
 *      ●定制 HttpMessageConverter 来实现多端内容协商
 *      ●编写 WebMvcConfigurer 提供的 configureMessageConverters 底层,修改底层的 MessageConverter
 *
 * 标注了 @ResponseBody 注解的由 HttpMessageConverter 处理
 * 标注了 @ResponseBody 的返回值将会由支持它的 HttpMessageConverter 写给浏览器
 *
 * 因为 所有的 访问都会先进入
 * @see org.springframework.web.servlet.DispatcherServlet
 * 的 doDispatch() 方法
 * 使用
 * @see RequestMappingHandlerAdapter 来执行  handleInternal()  在调用  invokeHandlerMethod() 来执行目标方法
 *   其中  if (this.argumentResolvers != null)  是参数解析器，用来确定目标方法的每个参数
 *          HandlerMethodArgumentResolver
 *   其中  if (this.returnValueHandlers != null) 是返回值处理器用来确定目标方法的返回值，该怎么处理
 *
 *   到 invocableMethod.invokeAndHandle(webRequest, mavContainer); 才会真正的执行目标方法,
 *   而此方法的 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 *   其中 returnValue 就是目标方法的返回值
 *
 *   到
 *   this.returnValueHandlers.handleReturnValue(
 * 					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
 * 	就开始执行 返回值处理器
 * 	        1. 找到一个合适的 处理器 HandlerMethodReturnValueHandler
 * 	        2.找到 {@link RequestResponseBodyMethodProcessor} 能处理
 * 	            标注了 @ResponseBody 注解的方法
 *                  返回值处理器  15种 XxxMethodReturnValueHandler .
 *                  支持哪几种返回值(如 map，Model,ModelAndView，ModelMap等)
 *                  //可以自定义
 *                  先遍历 查看是否是异步的  再交给返回值处理器(默认15种)
 *                  0 = {ModelAndViewMethodRetumValueHandler@5835} // @5835 会随时变
 *                  1 = {ModelMethodProcessor@5836}
 *                  2 = {ViewMethodReturnValueHandler@5837}
 *                  3 = {ResponseBodyEmitterReturmValueHandler@5838}
 *                  4 = {StreamingResponseBodyReturnValueHandler@5839}
 *                  5 = {HttpEntityMethodProcessor@5840}
 *                  6 = {HttpHeadersReturValueHandler@5841}
 *                  7 = {CallableMethodRetumValueHandler@5842}
 *                  8 = {DeferredResultMethodRetumValueHandler@5843}
 *                  9 = {AsyncTaskMethodReturnValueHandler@5844}
 *                  10 = {ModelAttributeMethodProcessor@5845}
 *                        @ModelAttribute 注解(类似于) @RequestMapping
 *                        不过返回值会保存在隐含的Model里 不进行页面跳转
 *                  11 = {RequestResponseBodyMethodProcessor@5846}
 *                         有 @ResponseBody 注解的，用这个处理器
 *                  12 = {ViewNameMethodReturnValueHandler@5847}
 *                  13 = {MapMethodProcessor@5848}
 *                  14 = {ModelAttributeMethodProcessor@5849}
 * 	        3.调用的他的 handleReturnValue 方法的
 * 	            writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
 *              来处理返回值
 *              他会 想找到合适的内容协商  HttpMessageConverter
 *              在遍历 {@link HttpMessageConverter} 默认有 0-7 ，xml 也是引入了 jackson-dataformat-xml 包
 *                      //需要新增自己的
 *                      0 = {ByteArrayHttpMessageConverter@11392} //支持字节数据读写
 *                      1 = {StringHttpMessageConverter@11421}    //支持字符串读写
 *                      2 = {StringHttpMessageConverter@11422}
 *                      3 = {ResourceHttpMessageConverter@11423}  //支持资源读写
 *                      4 = {ResourceRegionHttpMessageConverter@11424} //支持分区资源写出
 *                      5 = {AllEncompassingFormHttpMessageConverter@11425}  //支持表单 xml/json 读写
 *                      6 = {MappingJackson2HttpMessageConverter@11426} //支持json
 *                      7 = {MappingJackson2HttpMessageConverter@11427}
 *                      8 = {MappingJackson2XmlHttpMessageConverter@11428} //支持xml
 *                      9 = {MappingJackson2XmlHttpMessageConverter@11429}
 *                比如返回json 就会使用 第6个 写出 json
 *                在内部看，会发现 它使用 jackson 的 ObjectMapper 把对象写出去
 *              利用 MessageConverter 把返回值写出去
 *          4.{@link AbstractMessageConverterMethodProcessor}
 *
 *
 * 其中有个 HandlerAdapter 适配器,利用适配器执行
 *
 *
 */
@Controller
@ResponseBody
public class E2_自定义内容协商 {

    public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setAge(15);
        person.setUserName("李四");
        person.setBoss(true);
        person.setBirth(LocalDateTime.now());
        Map<String,Object> m = new HashMap<>();
        m.put("xxx",person);

        YAMLFactory yamlFactory = new YAMLFactory();
        yamlFactory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);//禁用文档开始标记： ---

        ObjectMapper objectMapper = new ObjectMapper(yamlFactory);
        String s = objectMapper.writeValueAsString(m);
        System.err.println(s);

    }

    //http://127.0.0.1:8080/yaml
    @GetMapping("/yaml")//默认 返回 JSON
    @ResponseBody
    public Object person(){
        Map<String,Object> re = new HashMap<>();
        Map<String,Object> xxx = new HashMap<>();
        re.put("xxx",xxx);
        xxx.put("dateTime",LocalDateTime.now().toString());
        xxx.put("name","张三");
        xxx.put("age",15);
        return re;
    }
    //http://127.0.0.1:8080/x-long
    @GetMapping("/x-long")//默认 返回 JSON
    @ResponseBody
    public Object x_long(){
        Person person = new Person();
        person.setAge(15);
        person.setUserName("李四");
        person.setBoss(true);
        person.setBirth(LocalDateTime.now());
        return person;
    }
}
